/*
 * hash_generator.c
 *
 *  Created on: Oct 5, 2011
 *      Author: mac
 */
#include "hash_generator.h"
#include <deque>
#include <list>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <assert.h>

list<int> _hash_entry;

//Hash table stored here
deque<list<int> > hash_table(INDEX_NUM, _hash_entry);

//Hash table size counter
int hash_table_counter = 0;

void hashTableGenerator(string ref_name) {
        //Open and read the reference file
        ifstream ref_file;
        deque<int>::iterator iter;
        int coor_counter = KEY_LENGTH*(-1);
        int flag_ignore = 0;
        int monitor_counter  = 0;       // For operation monitoring
        int monitor_counter2 = 0;       // For operation monitoring

        ref_file.open(ref_name.c_str());
        if (!ref_file.good()) {
                cerr << "reference file cannot open." << endl;
                exit(1);
        }

        //cout << "Hello0" << endl;

        //Initialize hash_table.
        //      hash_table.resize(1 << (2 * KEY_LENGTH) );
        //      hash_table_counter = 0;

        //cout << "Hello1" << endl;

        //Read in the first key
        char temp_key[KEY_LENGTH + 1];
        char temp_char;
        string keystr(KEY_LENGTH, 'A');
        //cout << "keystr: " << keystr << endl;

        //throw out the first several Ns
        do {
                ref_file >> temp_char;
                coor_counter++;
                //cout << "coor_counter: " << coor_counter << endl;
        } while (temp_char == 'N');

        //update the first non N character
        temp_key[0] = temp_char;
        //cout << "temp_char: " << temp_char << endl;

        //Read the first 12 characters
        for (int i = 1; i < KEY_LENGTH; i++) {
                //cout << "i: " << i << endl;
                ref_file >> temp_char;
                coor_counter++;
                //cout << "coor_counter: " << coor_counter << endl;
                temp_key[i] = temp_char;

                //Check if the file is too short
                if (ref_file.eof()) {
                        cerr << "file too short";
                        exit(1);
                }
                //Check if next N come too close
                if (temp_char == 'N') {
                        cerr << "Too short useful String A" << endl;
                        exit(1);
                }
        }

        temp_key[KEY_LENGTH] = '\0';
        keystr = temp_key;
        //cout << "temp_key: " << temp_key << endl;
        //cout << "keystr: " << keystr << endl;

        //Read the ref file until the end of file.
        do {
                //Compute idx and hash_value
                if (flag_ignore == 0) {
                        int hash_value = hashVal(keystr);
                //cout << "hash_value: " << hash_value << endl;

                //Add to hash_table
                //cout << "coor_counter: " << coor_counter << endl;
                        hash_table[hash_value].push_front(coor_counter);
                        hash_table_counter++;
                } else {
                        flag_ignore = 0;
                        for (int i = 0; i < KEY_LENGTH; i++ ) {
                                if (keystr[i] == 'N') {
                                        flag_ignore = 1;
                                }       
                        }
			if (flag_ignore == 0) {
                        	int hash_value = hashVal(keystr);
                        	//hash_table[hash_value].push_front(coor_counter);
                        	//hash_table_counter++;
			}
                }
                /*
                 //Add to hash_table list
                 if (hash_table[idx][hash_value].size() == 0) {
                 //cout << "first saw" << endl;
                 hash_table[idx][hash_value].push_back(counter);
                 }
                 else {
                 iter = hash_table[idx][hash_value].begin();
                 while (iter != hash_table[idx][hash_value].end() && counter > *iter) {
                 //cout << "*iter: " << *iter << endl;
                 iter++;
                 }
                 hash_table[idx][hash_value].insert(iter, counter);
                 }
                 */

                //Read next character
                ref_file >> temp_char;
                //Increment counter
                coor_counter++;

                bool fresh_start = false; //indicater for a new start after large area of N
                //If enters N area, throw them away
                while (temp_char == 'N') {
                        if (ref_file.eof() ) {
                                ref_file.close();
                                return;
                        }
                        fresh_start = true;
                        ref_file >> temp_char;
                        coor_counter++;
                        //cout << "coor_counter: " << coor_counter << endl;
                }
                if (fresh_start) {
                        //cout << "In a fresh start!" << endl;
                        keystr[0] = temp_char;
                        for (int i = 1; i < KEY_LENGTH; i++) {
                                //cout << 'i' << i << endl;
                                ref_file >> temp_char;
                                coor_counter++;
                                //cout << "coor_counter: " << coor_counter << endl;
                                //Check if next N come too close
                                if (temp_char == 'N') {
                                        cerr << "Too short useful String B" << endl;
                                        flag_ignore = 1;
                                        //exit(1);
                                }
                                keystr[i] = temp_char;
                        }
                } else
                        keystr = keystr.substr(1, KEY_LENGTH - 1) + temp_char;
                //cout << "keystr: " << keystr << endl;
                // add process monitoring loop
                monitor_counter  = monitor_counter  +1;         // For operation monitoring
                monitor_counter2 = monitor_counter2 +1;         
                if (monitor_counter >= 1000000) {
                        cout << "hash generator: " << monitor_counter2 << endl;
                        monitor_counter = 0;
                }
        } while (ref_file.good());

        ref_file.close();
}

void hashFileWriter(string hash_name) {
        int monitor_counter = 0;        // for operation monitoring
        float monitor_counter2 = 0;       // for operation monitoring
        ofstream store_file;
        store_file.open(hash_name.c_str());
        store_file << hash_table_counter + INDEX_NUM << endl;
		//cout << "total hash_counter: " << hash_table_counter << endl;
		//cout << "total INDEX_NUM   : " << INDEX_NUM << endl;
	
        for (int j = 0; j < INDEX_NUM; j++) {
                int entry_size = hash_table[j].size();
                store_file << entry_size << endl;
                monitor_counter  = monitor_counter  + 1;  
                while (!hash_table[j].empty()) {
                        store_file << hash_table[j].back() << " ";
                        hash_table[j].pop_back();
                	monitor_counter  = monitor_counter  + 1;  
                	if (monitor_counter >= (hash_table_counter + INDEX_NUM)/1000) {
                		monitor_counter2 = monitor_counter2 + 0.1;
                        	fprintf(stdout, "hash writer: %2.1f%% \n", monitor_counter2); 
                        	monitor_counter = 0;
                	}
                }
                if (monitor_counter >= (hash_table_counter + INDEX_NUM)/1000) {
                	monitor_counter2 = monitor_counter2 + 0.1;
                       	fprintf(stdout, "hash writer: %2.1f%% \n", monitor_counter2); 
                       	monitor_counter = 0;
                }
                if (entry_size != 0)
                        store_file << endl;
        }
		hash_table_counter = 0;
        store_file.close();
}
